#include "rpc/net/module/poll.h"

namespace rpc {
bool Poll::Init() {
  if (!fds_.empty()) return false;

  struct pollfd pfd = { -1, 0, 0 };
  fds_.resize(MaxEventCnt, pfd);

  return true;
}

bool Poll::Add(Event* ev, int flag) {
  if (ev == NULL || flag < 0) return false;
  CHECK(ev->fd != -1);

  struct pollfd* pfd = &fds_[ev->fd];
  if (pfd->fd != -1) return false;
  pfd->fd = ev->fd;
  if (flag & EV_READ) {
    pfd->events |= POLLIN;
  }
  if (flag & EV_WRITE) {
    pfd->events |= POLLOUT;
  }

  return events_.insert(std::pair<int, Event*>(ev->fd, ev)).second;
}

void Poll::Del(Event* ev, int flag) {
  if (ev == NULL || flag < 0) return;
  CHECK(ev->fd != -1);

  struct pollfd* pfd = &fds_[ev->fd];
  if (pfd->fd == -1) return;
  pfd->fd = -1;
  pfd->events = 0;
  pfd->revents = 0;

  events_.erase(ev->fd);
}

bool Poll::Mod(Event* ev, int flag) {
  if (ev == NULL || flag < 0) return false;
  CHECK(ev->fd != -1);

  struct pollfd* pfd = &fds_[ev->fd];
  if (pfd->fd == -1) return Add(ev, flag);

  if (flag & EV_READ) {
    pfd->events |= POLLIN;
  }
  if (flag & EV_WRITE) {
    pfd->events |= POLLOUT;
  }

  return true;
}

void Poll::Loop() {
  loop_tid_ = ::pthread_self();
  exit_ = false;
  while (!exit_) {
    if (events_.empty()) {
      ::sleep(1);
      continue;
    }

    int trigged_cnt = ::poll(fds_.data(), fds_.size(), 1000);
    if (trigged_cnt == -1) {
      if (errno != EINTR) {
        PLOG(WARNING)<< "poll error";
      }
      continue;
    }

    int i = 0;
    for (std::map<int, Event*>::iterator it = events_.begin();
        i < trigged_cnt && it != events_.end(); ++it) {
      Event* ev = it->second;
      CHECK_EQ(it->first, ev->fd);
      CHECK(fds_[ev->fd].fd != -1);

      short event = 0;
      if (fds_[ev->fd].revents & POLLIN) {
        event |= EV_READ;
      }
      if (fds_[ev->fd].revents & POLLOUT) {
        event |= EV_WRITE;
      }
      if (event) {
        i++;
        ev->cb(ev->fd, ev->arg, event);
      }
    }
    HandleCb();
  }
}

}
